Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / network / src / commonMain / kotlin / org / meshtastic / core / network / radio / GattCacheInvalidationGate.kt

Displaying Raw • Download

core/network/src/commonMain/kotlin/org/meshtastic/core/network/radio/GattCacheInvalidationGate.kt bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 5.65 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.network.radio

T8b949e/**
* Decides when the ordinary BLE reconnect loop should refresh the platform's cached GATT service table.
*
* Background: after a bonded radio goes out of range or power-cycles for a long time, Android can keep serving a stale
* cached service table for it. Every reconnect then discovers the wrong (or no) Meshtastic characteristics and fails,
* so [BleReconnectPolicy] retries forever and the UI never leaves "Not connected" — historically only an OS-level
* unpair/re-pair cleared it.
*
* Refreshing is not free: it costs an extra disconnect → settle → reconnect round trip and throws away a valid cache,
* so it must not fire on the ordinary out-of-range blip. A radio switched off for a minute, or a phone carried out of
* range and back, is not evidence of a stale cache — tearing down a link that was about to succeed would turn an
* ordinary reconnect into a failure plus a fresh round of backoff, which is worse than doing nothing at all.
*
* The gate therefore arms only once the failure streak has run for *minutes* (see [DEFAULT_FAILURE_THRESHOLD]) — the
* prolonged absence issue #6685 actually describes — and fires **at most once per streak**: if a genuine refresh did
* not fix the connection, repeating it every attempt only adds latency.
*
* Not thread-safe by design: it is owned by [BleRadioTransport]'s single reconnect-loop coroutine.
*
* @param failureThreshold consecutive reconnect failures required before a refresh is considered
*/
Tff7b72internal Tff7b72class T56d364GattCacheInvalidationGateTb4b4b4(Tff7b72private Tff7b72val Te6edf3failureThresholdTb4b4b4: Tffa657Int Tff7b72= Te6edf3DEFAULT_FAILURE_THRESHOLDTb4b4b4) Tb4b4b4{

Tff7b72init Tb4b4b4{
Te6edf3requireTb4b4b4(Te6edf3failureThreshold Tff7b72> T79c0ff0Tb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6fffailureThreshold must be positive, was Tffd700$Te6edf3failureThresholdTa5d6ff" Tb4b4b4}
Tb4b4b4}

T8b949e/** True once a refresh actually took effect during the current failure streak. */
Tff7b72private Tff7b72var Te6edf3invalidatedForCurrentStreakTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72false

T8b949e/**
* Returns true when the connection attempt now in progress should refresh the GATT cache.
*
* A [consecutiveFailures] of zero can never reach [failureThreshold] (which is always positive), so a healthy
* connection never refreshes.
*
* @param consecutiveFailures failures observed *before* the attempt in progress, i.e.
* [BleReconnectPolicy.consecutiveFailures] read from inside the attempt
*/
Tff7b72fun Td2a8ffshouldInvalidateOnAttemptTb4b4b4(Te6edf3consecutiveFailuresTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Tff7b72!Te6edf3invalidatedForCurrentStreak Tff7b72&Tff7b72& Te6edf3consecutiveFailures Tff7b72>Tff7b72= Te6edf3failureThreshold

T8b949e/**
* Records that a refresh actually took effect, consuming this streak's single allowance.
*
* Only call this when the platform reported success. A refresh that could not be performed at all (on Android the
* reflection hop into the `BluetoothGatt` can miss after a Kable upgrade) is a no-op that costs nothing, so it must
* not burn the allowance — otherwise one silent miss would disable the recovery for the rest of the streak.
*/
Tff7b72fun Td2a8ffonCacheInvalidatedTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3invalidatedForCurrentStreak Tff7b72= Tff7b72true
Tb4b4b4}

T8b949e/**
* Re-arms the gate because the failure streak ended, so a later streak earns its own refresh.
*
* Must be driven by the same outcomes that reset [BleReconnectPolicy.consecutiveFailures]. The counter cannot be
* used to infer this: the caller only reads it on an attempt that reached a connected link, and the streak-ending
* reset lands *after* that attempt returns. A radio that is out of range again fails its next attempt long before
* any zero could be observed, which would leave the gate consumed forever and silently reintroduce issue #6685 the
* second time a device disappears.
*/
Tff7b72fun Td2a8ffonFailureStreakEndedTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3invalidatedForCurrentStreak Tff7b72= Tff7b72false
Tb4b4b4}

Tff7b72companion Tff7b72object Tb4b4b4{
T8b949e/**
* Consecutive failures before a stale cache is suspected.
*
* Deliberately far above [BleReconnectPolicy.DEFAULT_FAILURE_THRESHOLD] (3), which only marks a disconnect as
* "more than a blip" for the UI. Three failures are reached about 47 s into an ordinary out-of-range or
* powered-off gap, so refreshing there would sabotage a normal reconnect rather than repair a stale cache.
*
* Six is the first count at which [computeReconnectBackoff] has been saturated at its 60 s cap for two
* consecutive cycles: the retry ladder is exhausted and every further attempt is identical. With a
* [BleReconnectPolicy.DEFAULT_SETTLE_DELAY] before each attempt, the refresh lands on the seventh attempt, 3
* min 36 s into an unbroken streak (seven settle delays plus 5+10+20+40+60+60 s of backoff). That is minutes of
* continuous failure, which is what issue #6685's "out of range / power off" report describes, rather than the
* sub-minute blip the lower threshold catches.
*/
Tff7b72const Tff7b72val Te6edf3DEFAULT_FAILURE_THRESHOLD Tff7b72= T79c0ff6
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.2 - Generated in 0.09s